﻿using System.Linq.Expressions;

namespace EOrm
{
    /// <summary>
    /// sql构建器
    /// </summary>
    public abstract class SqlCreator
    {

        /// <summary>
        /// 构建选择目标数据表所有记录的sql语句
        /// </summary>
        /// <param name="table">目标数据表</param>
        /// <returns>选择目标数据表所有记录的sql语句</returns>
        public abstract string Select(ITable table);

        /// <summary>
        /// 构建按条件选择目标数据表并按条件排序记录的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="whereExp">选择条件表达式</param>
        /// <returns>按条件选择目标数据表并按条件排序记录的sql语句</returns>
        public abstract string Select<T>(ITable table, Expression<Func<T, bool>>? whereExp) where T : class;

        /// <summary>
        /// 构建按条件选择目标数据表并按条件排序记录的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <typeparam name="U">目标数据字段</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="whereExp">选择条件表达式</param>
        /// <param name="orderByExp">排序表达式</param>
        /// <returns>按条件选择目标数据表并按条件排序记录的sql语句</returns>
        public abstract string Select<T, U>(ITable table, Expression<Func<T, bool>> whereExp, Expression<Func<T, U>> orderByExp) where T : class;

        /// <summary>
        /// 构建按条件选择目标数据表并按条件排序记录的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <typeparam name="U">目标数据字段</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="whereExp">选择条件表达式</param>
        /// <param name="orderByExp">排序表达式</param>
        /// <param name="limit">行数限制</param>
        /// <returns>按条件选择目标数据表并按条件排序记录的sql语句</returns>
        public abstract string Select<T, U>(ITable table, Expression<Func<T, bool>>? whereExp, Expression<Func<T, U>>? orderByExp, int limit) where T : class;

        /// <summary>
        /// 构建删除对应单一实体的sql语句
        /// </summary>
        /// <typeparam name="T">实体泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="entity">要删除的实体</param>
        /// <returns>删除对应单一实体的sql语句</returns>
        public abstract string DeleteOne<T>(ITable table, T? entity) where T : class;

        /// <summary>
        /// 构建删除对应列表实体的sql语句
        /// </summary>
        /// <typeparam name="T">实体泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="entities">要删除的实体列表</param>
        /// <returns>删除对应列表实体的sql语句</returns>
        public abstract string DeleteList<T>(ITable table, List<T>? entities
            ) where T : class;

        /// <summary>
        /// 构建按条件删除目标记录的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="whereExp">选择条件表达式</param>
        /// <returns>按条件删除目标记录的sql语句</returns>
        public abstract string Delete<T>(ITable table, Expression<Func<T, bool>>? whereExp) where T : class;

        /// <summary>
        /// 构建修改对应实体的sql语句
        /// </summary>
        /// <typeparam name="T">实体泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="entity">要修改的实体</param>
        /// <returns>修改对应实体的sql语句</returns>
        public abstract string Update<T>(ITable table, T? entity) where T : class;

        /// <summary>
        /// 构建添加对应实体的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="entity">要添加的实体</param>
        /// <returns>添加对应实体的sql语句</returns>
        public abstract string Insert<T>(ITable table, T? entity) where T : class;

        /// <summary>
        /// 构建添加对应实体并返回主键的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="entity">要添加的实体</param>
        /// <returns>添加对应实体并返回主键的sql语句</returns>
        public abstract string InsertReturnKey<T>(ITable table, T? entity) where T : class;

        /// <summary>
        /// 构建按条件统计记录的sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <param name="table">目标数据表</param>
        /// <param name="whereExp">选择条件表达式</param>
        /// <returns>按条件统计记录的sql语句</returns>
        public abstract string Count<T>(ITable table, Expression<Func<T, bool>>? whereExp) where T : class;

        /// <summary>
        /// 构建分页sql语句
        /// </summary>
        /// <typeparam name="T">目标数据表泛型</typeparam>
        /// <typeparam name="U">目标排序字段泛型</typeparam>
        /// <param name="table">数据表</param>
        /// <param name="whereExp">选择条件表达式</param>
        /// <param name="orderBy">排序条件表达式</param>
        /// <param name="limit">行数限制</param>
        /// <param name="offset">跃进行数</param>
        /// <returns>分页sql语句</returns>
        public abstract string Page<T, U>(ITable table, Expression<Func<T, bool>>? whereExp = null, Expression<Func<T, U>>? orderBy = null, int limit = 10, int offset = 0) where T : class;

        //=======================================================================

        /// <summary>
        /// 添加sql缓存语句的锁
        /// </summary>
        protected static readonly string _addCacheSqlLock = "addCacheSqlLock";

        /// <summary>
        /// 添加sql记录语句的锁
        /// </summary>
        protected static readonly string _addHistorySqlLock = "addHistorySqlLock";

        /// <summary>
        /// sql语句缓存
        /// </summary>
        protected static Dictionary<string, string> CacheSqls = new Dictionary<string, string>();

        /// <summary>
        /// sql语句记录
        /// </summary>
        protected static List<string> HistorySqls = new List<string>();

        /// <summary>
        /// sql记录语句最多存储量
        /// </summary>
        protected static int _maxSqlStorage = 100;

        /// <summary>
        /// 获取最近一次执行的sql语句
        /// </summary>
        /// <returns>最近一次执行的sql语句</returns>
        public static string GetLastHistorySql()
        {
            if (HistorySqls != null && HistorySqls.Count > 0)
            {
                return HistorySqls.Last();
            }
            return "";
        }

        /// <summary>
        /// 获取所有记录的执行过的sql语句
        /// </summary>
        /// <returns>所有记录的执行过的sql语句</returns>
        public static List<string> GetAllHistorySql()
        {
            if (HistorySqls==null)
            {
                return new List<string>();
            }
            else
            {
                return HistorySqls;
            }
        }

        /// <summary>
        /// 向sql语句记录中添加sql语句
        /// </summary>
        /// <param name="sql">要添加的sql语句</param>
        internal static void AddHistorySql(string sql)
        {
            if (HistorySqls.Count>=_maxSqlStorage)
            {
                HistorySqls.RemoveAt(0);
            }
            lock (_addHistorySqlLock)
            {
                HistorySqls.Add(sql);
            }
        }
        /// <summary>
        /// 获取缓存中指定键值的sql语句
        /// </summary>
        /// <param name="key">缓存键值</param>
        /// <returns>缓存的sql语句</returns>
        internal static string GetSqlCache(string key)
        {
            if (CacheSqls.ContainsKey(key))
            {
                return CacheSqls[key];
            }
            return "";
        }

        /// <summary>
        /// 判定是否存在指定键值的sql语句
        /// </summary>
        /// <param name="key">sql语句键值</param>
        /// <returns>判定结果</returns>
        internal static bool CacheContainsKey(string key)
        {
            return CacheSqls.ContainsKey(key);
        }

        /// <summary>
        /// 添加sql缓存,同时解除sql注入攻击并返回
        /// </summary>
        /// <param name="key">sql缓存的键</param>
        /// <param name="sql">要缓存的sql语句</param>
        /// <returns>解除注入攻击后的sql语句</returns>
        internal static string AddCacheSql(string key, string sql)
        {
            if (key == null || string.IsNullOrEmpty(key.Trim()) || sql == null || string.IsNullOrEmpty(sql.Trim()))
            {
                throw new ArgumentNullException("key或sql", "要添加的sql为空或没有指定键值");
            }
            sql = sql.Replace("'", "\\'").Replace("<%", "'").Replace("%>", "'");
            if (!CacheSqls.ContainsKey(key))
            {
                lock (_addCacheSqlLock)
                {
                    if (!CacheSqls.ContainsKey(key))
                    {
                        CacheSqls.Add(key, sql);
                    }
                }
            }
            return sql;
        }
    }
}
